package com.cmz.common.utils;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.cmz.common.enums.CommonError;
import com.cmz.common.exception.ServiceRuntimeException;
import org.apache.commons.lang3.StringUtils;
import org.yaml.snakeyaml.Yaml;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

/**
 * YAML 格式处理工具
 *
 * @author cmz
 */
public class YamlUtils {

    private YamlUtils() {
    }

    public static Map read(File file) throws FileNotFoundException {
        InputStream yamlInputStream = new FileInputStream(file);
        Yaml yaml = new Yaml();
        return yaml.load(yamlInputStream);
    }

    public static <T> T read(File file, Class<T> type) throws FileNotFoundException {
        InputStream yamlInputStream = new FileInputStream(file);
        Yaml yaml = new Yaml();
        return yaml.loadAs(yamlInputStream, type);
    }

    public static void write(File target, Map<String, Object> propertiesMap) {
        List<String> propertiesLines = Lists.newArrayList();
        for (String key : propertiesMap.keySet()) {
            propertiesLines.add(key + "=" + propertiesMap.get(key));
        }
        String result = propertiesToYml(propertiesLines);

        try (FileOutputStream fileOutputStream = new FileOutputStream(target);
             OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream);
             BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter)) {
            bufferedWriter.write(result);
        } catch (IOException e) {
            throw new ServiceRuntimeException(CommonError.XIOSDKCM0001, e);
        }
    }

    public static Map<String, Object> readAsKeyVal(String content) {
        Map<String, Object> resultMap = Maps.newLinkedHashMap();

        Yaml yaml = new Yaml();
        Map<String, Object> rawMap = yaml.load(content);

        parseYamlMap(rawMap, null, resultMap);

        return resultMap;
    }

    public static Map<String, Object> readAsKeyVal(File file) throws FileNotFoundException {
        Map<String, Object> resultMap = Maps.newLinkedHashMap();

        InputStream yamlInputStream = new FileInputStream(file);
        Yaml yaml = new Yaml();
        Map<String, Object> rawMap = yaml.load(yamlInputStream);

        parseYamlMap(rawMap, null, resultMap);

        return resultMap;
    }

    private static void parseYamlMap(Object value, String currentKey, Map<String, Object> result) {
        if (value instanceof Map) {
            for (String key : ((Map<String, Object>) value).keySet()) {
                String nextKey = StringUtils.isEmpty(currentKey) ? key : currentKey + "." + key;
                parseYamlMap(((Map<String, Object>) value).get(key), nextKey, result);
            }
        } else if (value instanceof List) {
            for (int i = 0; i < ((List) value).size(); i++) {
                String nextKey = StringUtils.isEmpty(currentKey) ? "[" + i + "]" : currentKey + "[" + i + "]";
                parseYamlMap(((List) value).get(i), nextKey, result);
            }
        } else {
            result.put(currentKey, value);
        }
    }

    public static String propertiesToYml(List<String> lines) {
        //使用treemap排好序
        Map<String, String> sourceMap = new TreeMap<String, String>();
        for (String line : lines) {
            if (!StringUtils.isEmpty(line) && !line.startsWith("#")) {
                String key = line.substring(0, line.indexOf("="));
                String value = line.substring(line.indexOf("=") + 1);
                sourceMap.put(key, value);
            }
        }
        Iterator<String> it = sourceMap.keySet().iterator();
        //保存yml的行内容
        List<String> ymlLines = new ArrayList<String>();
        //Tab用两个空格格式化
        String tab = "  ";

        //yml文档树
        Map<String, List<String>> treeMap = new TreeMap<String, List<String>>();
        //父级名称
        String parent = "";
        //子节点列表
        List<String> element = new ArrayList<String>();

        while (it.hasNext()) {
            String key = it.next();
            //.拆分key
            String[] keys = key.split("\\.");
            String prefix = "";
            for (int i = 0; i < keys.length; i++) {
                //从第二个节点开始，行前面需要加tab来格式化，并设置它的父节点
                if (i > 0) {
                    parent += keys[i - 1];
                    prefix += tab;
                }
                String line = prefix + keys[i] + ": ";
                if (treeMap.get(parent) == null) {
                    treeMap.put(parent, new ArrayList<String>());
                }
                if (!treeMap.get(parent).contains(line)) {
                    element = treeMap.get(parent) == null ? new ArrayList<String>() : treeMap.get(parent);
                    if (!element.contains(line)) {
                        element.add(line);
                        treeMap.put(parent, element);
                    }
                    if (i == keys.length - 1) {
                        ymlLines.add(line + sourceMap.get(key) + "\n");
                        parent = "";
                    } else {
                        ymlLines.add(line + "\n");
                    }
                }
            }
        }

        StringBuilder stringBuilder = new StringBuilder();
        for (String line : ymlLines) {
            stringBuilder.append(line);
        }
        return stringBuilder.toString();
    }


    /**
     * 自定义规则
     * 1.内容不得为空，不得只包含换行或者空格
     * 2.去掉所有空格
     * 3.去掉空行和以“#”开头的注释行
     * 4.一行必须有且唯一一个赋值“=”符号，且不得处于字符串的开头或结尾
     * 5.不得只存在注释，不存在配置内容
     * todo:这里直接返回RuntimeException，是否需要在CommonError里面定义错误码,同改errors.properties
     *
     * @param conStr 内容字符串
     * @return KV Map
     */
    public static Map<String, Object> readPropertiesAsKeyVal(String conStr) {
        Map<String, Object> resultMap = Maps.newLinkedHashMap();

        if (conStr == null || "".equals(conStr)) {
            throw new RuntimeException("配置内容不得为空");
        }
        String repStr = conStr.replaceAll(" ", "").replaceAll("\n", "");
        if ("".equals(repStr)) {
            throw new RuntimeException("配置内容不得为空");
        }
        String content = conStr.replaceAll(" ", "");
        String[] properties = content.split("\n");
        int rowNum = 1;
        int flag = 0;
        for (String s : properties) {
            if ("".equals(s) || s == null || "#".equals(s.substring(0, 1))) {
                rowNum++;
                continue;
            }

            flag++;
            if (s.indexOf("=") > 0 && s.indexOf("=") != s.length() - 1) {
                String[] tmp = s.split("=");
                if (tmp.length > 2) {
                    String errStr = "语法错误，见第" + rowNum + "行:" + s + "。错误原因:\"=\"出现了不止一次。";
                    throw new RuntimeException(errStr);
                }
                resultMap.put(tmp[0], tmp[1]);
            } else {
                String errStr = "语法错误，见第" + rowNum + "行:" + s + "。错误原因:\"=\"不存在或处于语句开头/结尾";
                throw new RuntimeException(errStr);
            }

            rowNum++;
        }

        if (flag == 0) {
            throw new RuntimeException("除注释外，不存在配置内容，请检查");
        }

        return resultMap;
    }

    public static Map<String, Object> readAsKeyVal(String conStr, String fileType) {
        return YamlUtils.readAsKeyVal(conStr);
    }

}
